<template>
  <a-table class="permissionTable" :defaultExpandAllRows="true" :columns="columns" :data-source="dataSource"
    :row-key="props.rowKey" @expand="onTableExpand" :scroll="props.scroll" :pagination="false">

    <template #headerCell="{ column, record }">
      <div class="topLevel">
        <a-checkbox v-model:checked="column.selected" @change="topSelect(column, record)" :key="column.index"
          :class="column.className" :disabled="!props.isAssignable" v-if="!column.uncheckAble">
          {{ column.title }}
        </a-checkbox>
        <span v-else>{{
          column.title }} </span>
      </div>
    </template>
    <template #bodyCell="{ column, record }">

      <div v-if="column.dataIndex == 'name'" class="dataName">
        <a-checkbox v-model:checked="record.hasPerm" @change="handleSiteNamePerm(column, record)"
          :class="record.className" :disabled="!props.isAssignable || !record.assignable">
          <div v-if="record.id == '-1'">
            {{ record.name }}
            <a-tooltip placement="right">
              <template #title>
                <span>选中后，用户可拥有后续新增子站点的权限</span>
              </template>
              <i class="iconfont icon-hint1" style="font-size: 16px; margin-left: 8px">
              </i>
            </a-tooltip>
          </div>
          <div v-else>{{ record.name }}</div>
        </a-checkbox>
      </div>
      <span v-else-if="column.uncheckAble">{{ record[column.dataIndex] }}</span>
      <a-checkbox v-else-if="column.index >= 0" v-model:checked="record.linePerms[column.index].hasPerm"
        @change="handleOtherPerm(column, record)"
        :disabled="!props.isAssignable || !record.linePerms[column.index].assignable">
      </a-checkbox>

    </template>
  </a-table>
  <pagination @selectAll="selectAll" :dataSource="props.dataSource" @changePage="changePage"
    :isSelect="selectedRowKeys.length" :total="props.total" :selfChecked="selfChecked" :scroll="scroll"
    :showCheckDelete="props.showCheckDelete" v-if="(props.isShowPagination && props.dataSource.length)">
    <template #opration>

      <slot name="otherOpration"></slot>
    </template>
  </pagination>
</template>
<script lang="ts" setup>
import { ref, watch, nextTick } from 'vue';
import { baseProps } from './tableConfig/props';
import pagination from "@/components/pagination/index.vue";
import { Console } from 'console';
// type
interface dataSourceType {
  [propName: string]: any;
}
// props
const props = defineProps({
  ...baseProps, isAssignable: {
    type: Boolean,
    default: true,
  }
});
//emit
const emit = defineEmits(['deleteRecord', 'changePage', 'moveRecord', 'expanded']);

//interface
interface DataList {
  [propName: string | number]: any;
}
//type
type tableextend = (a: Boolean, b: Object) => void;


//data
let dataSource = ref<DataList[]>([]);

const columns = ref(props.columns);
const loading = ref(true);
let arrObj: DataList = {
  name: [],
  seePer: [],
}
columns.value.forEach((item: DataList, index: number) => {
  if (item.key != 'see' && item.key != 'name') {
    arrObj['ohterPer' + (item.index)] = [];
  }
});
// watch
watch(() => props.columns, (newVal, oldVal) => {
  if (newVal !== oldVal) {
    columns.value = newVal;
  }
}, { deep: true })
watch(() => props.dataSource, (newVal, oldVal) => {
  if (newVal !== oldVal) {
    loading.value = true;
    let originData: DataList[] = [];
    loopFloat(JSON.parse(JSON.stringify(newVal)), originData, []);

    nextTick(() => {
      dataSource.value = originData;
      checkSelect({}, dataSource.value);// 数据更新，初始化的时候需要处理第一行和第一列的选中和非选
    })
    nextTick(() => {
      loading.value = false;
    })
  }

}, { deep: true })


//methods
/**
 * 对于第一行的选中与否，根据columns的selected属性响应式，选中、半选、非选的视图变化根据isSelectAll()方法返回的数组决定， 长度为2，则是半选， 
   长度为一，且为true则为全选，否则非选，用isSelectAll()来决定视图是因为第一行是纯展示，和表格数据无相关性
 * 对于第一列的选中与否，在基础数据中添加了hasPerm用来响应式选中与否，至于半选，则根据isSelectAll()的长度来决定， 因为是在基础数据中添加的hasPerm，
   所以第一列的选中与否与基础数据相关，全选和非选都由基础数据决定
 * 对于查看列，父级查看非选，父级以及其子项所有项都非选
 * 对于查看列之后的列，选中时，对应查看列选中
 * 对于所有操作项，选中时，其父级以及祖父级查看项相应选中，即查看权限要向上冒泡（parentNodes），也要向下冒泡（childNodes）
 * 
 * **/
const topSelect = (column: DataList, record: DataList) => {
  nextTick(() => {
    loop(dataSource.value, record, column.index, column.selected);
  })
  checkSelect(column, dataSource.value);
}
const handleSiteNamePerm = (column: DataList, record: DataList) => {
  nextTick(() => {
    loop(dataSource.value, record, column.index, record.hasPerm);
  })
  checkSelect(column, dataSource.value);
}
const handleOtherPerm = (column: DataList, record: DataList) => {
  nextTick(() => {
    loop(dataSource.value, record, column.index, record.linePerms[column.index].hasPerm);
  })
  checkSelect(column, dataSource.value);
}
const loop = (data: DataList[] = [], record: DataList, column_index: number, isSelected: Boolean | any) => {

  for (let i in data) {
    if (!record) {
      if (column_index == -1) { // 标明是第一行第一列，这时候要全选或全不选
        data[i].hasPerm = isSelected;// 第一列更改状态
        data[i].linePerms.forEach((item: DataList, index: number) => {// 第二列及后续列更改状态
          item.hasPerm = isSelected
        })
      } else if (column_index == 0) { // 点击了第一行查看按钮，当isSelected为true是，所有的查看选中
        data[i].linePerms.forEach((item: DataList, index: number) => {// 第二列及后续列更改状态
          if (!isSelected) { // 当查看非选时，所有项都非选
            data[i].hasPerm = isSelected;
            item.hasPerm = isSelected
          } else { // 当查看选中时，所有查看项都选中
            data[i].hasPerm = isSelected;
            if (index == column_index) {
              item.hasPerm = isSelected
            }

          }
        })
      } else { // 当选择第3列及以后，只更改当列的所有行状态
        data[i].linePerms.forEach((item: DataList, index: number) => {// 第三列及后续列更改状态
          if (column_index == index) {
            item.hasPerm = isSelected //更改所在列的状态
          }
          if (isSelected) { // 当为选中时，所有的查看都要选中
            if (index == 0) {
              item.hasPerm = isSelected //更改查看所在列的状态
            }
          }
        })

      }
      // if (data[i].children instanceof Array && data[i].children.length) { // 所有子项也要更改状态
      //   loop(data[i].children, record, column_index, isSelected);
      // }

    } else if (record) {
      //data[i].parentNodes.includes(data[i].id), 说明是子集
      if (column_index <= 0) {
        if ((record.id == data[i].id || data[i].parentNodes.includes(record.id) || record.parentNodes.includes(data[i].id))) {
          if (column_index == -1) {// 点击了第一列
            data[i].hasPerm = isSelected;// 第一列更改状态
            data[i].linePerms.forEach((item: DataList, index: number) => {// 第二列及后续列更改状态
              item.hasPerm = isSelected
            })
          } else if (column_index == 0) { // 点击查看列
            data[i].hasPerm = isSelected;
            data[i].linePerms.forEach((item: DataList, index: number) => {// 第二列及后续列更改状态
              if (isSelected) {//选中查看列时， 第一列和查看列选中
                if (index == 0) {
                  item.hasPerm = isSelected;
                }
              } else {//查看列非选时， 本层级及子集所有项非选
                !record.parentNodes.includes(data[i].id) ? item.hasPerm = isSelected : '';

              }
            })
          }
        }
      } else {// 点击第三列及以后，当选中时，影响当前行的查看以及当前行的第一列（以及其父级、父级的父级前两列），非选时，只影响当前点击
        if ((record.id == data[i].id || record.parentNodes.includes(data[i].id))) {
          data[i].linePerms.forEach((item: DataList, index: number,) => {
            if (isSelected) {
              // 选中时，所在行的前两列应为选中
              data[i].hasPerm = isSelected;
              index == 0 ? item.hasPerm = isSelected : '';
            }
            // 点击选中必须要id相同
            index == column_index && record.id == data[i].id ? item.hasPerm = isSelected : '';
          });
        }
      }

    }

    //
  }
}
const loopData = (data: DataList[], column_index: (number | string)[] = []) => { // 处理第一行和第一列的选中和非选
  data.forEach((item: DataList) => {
    let arr = isSelectAll([item], { name: [], seePer: [], ohterPer: [] }, column_index);
    item.hasPerm = arr.name.includes(true) ? true : false;
    item.className = arr.name.includes(true) && arr.name.length > 1 ? 'halfSelect' : '';
    if (item.children instanceof Array && item.children.length) {
      loopData(item.children, column_index);
    }
  })
}
const isSelectAll = (record: any[], arr: DataList, column_index: (number | any)[] = [],) => {
  let arrays = arr;
  for (let i in record) {
    record[i].linePerms.forEach((item: DataList, index: number) => {
      if (column_index.length) { // 说明是第一行的权限展示(不包括第一行的全选按钮)
        if (column_index.includes(index)) { // 这样column_index至少是从0开始的,对应列做检测
          if (index == 0) {
            arrays.seePer.includes(item.hasPerm) ? '' : arrays.seePer.push(item.hasPerm);
          } else if (index > 0) {
            if (arrays['ohterPer' + index]) {
              (arrays['ohterPer' + index].includes(item.hasPerm) ? '' : arrays['ohterPer' + index].push(item.hasPerm));
            } else {
              arrays['ohterPer' + index].push(item.hasPerm)
            }

          }
        }
        arrays.name.includes(item.hasPerm) ? '' : arrays.name.push(item.hasPerm);//全选按钮应该push所有权限
      } else {// 说明是第一列的，应该获取本身及子集的所有权限
        arrays.name.includes(item.hasPerm) ? '' : arrays.name.push(item.hasPerm);
      }
    })
    if (!column_index.length) {
      if (arrays.name.length > 1) {
        continue
      }
      let DataArr = JSON.parse(JSON.stringify(dataSource.value)); // 这块是为了遍历所有的子集，得出第一列的选中结果；
      DataArr.forEach((item: dataSourceType) => {
        if (record[i].childNodes.includes(item.id)) {
          item.linePerms.forEach((pers: dataSourceType) => {
            arrays.name.includes(pers.hasPerm) ? '' : arrays.name.push(pers.hasPerm);
          })
        }
      })
    }
  }
  return arrays;
}
const checkSelect = (column: DataList, record: DataList[]) => {
  nextTick(() => {
    let paramsArr = columns.value.map((item: DataList) => item.index);// 每次动一下数据，检测一下头部选择，配合虚拟列表也不会卡顿

    let arrays = isSelectAll(dataSource.value, JSON.parse(JSON.stringify(arrObj)), paramsArr);

    columns.value.forEach((item: dataSourceType) => {
      if (item.key == 'name') {
        item.selected = arrays.name.length > 1 || (arrays.name.length == 1 && arrays.name[0] === true) ? true : false;
        item.className = arrays.name.includes(true) && arrays.name.length > 1 ? 'halfSelect' : '';
      } else if (item.key == 'see') {
        item.selected = arrays.seePer.length > 1 || (arrays.seePer.length == 1 && arrays.seePer[0] === true) ? true : false;
        item.className = arrays.seePer.includes(true) && arrays.seePer.length > 1 ? 'halfSelect' : '';
      } else {
        item.selected = arrays['ohterPer' + item.index].length > 1 || (arrays['ohterPer' + item.index].length == 1 && arrays['ohterPer' + item.index][0] === true) ? true : false;
        item.className = arrays['ohterPer' + item.index].includes(true) && arrays['ohterPer' + item.index].length > 1 ? 'halfSelect' : '';
      }
    })
    loopData(record, []);
  })

}
// loop 扁平化站群、菜单数据, vxeTable只识别扁平化数据
const loopFloat = (data: dataSourceType[], list: dataSourceType[], pid: (string | number | any)[]) => {
  data.forEach((item: dataSourceType) => {

    item.parentNodes = JSON.parse(JSON.stringify(pid));
    if (item.parentId) {
      item.parentNodes.push(item.parentId);
    }
    item.childNodes = [];
    if (item.children instanceof Array && item.children.length) {
      getChildIds(item.children, item.childNodes);
    }
    let obj = Object.assign({
      id: item.id,
      name: item.name,
      parentId: item.parentId,
      linePerms: item.linePerms,
      parentNodes: item.parentNodes,
      childNodes: item.childNodes
    }, item)
    list.push(obj);
    item.children && item.children.length ? loopFloat(item.children, list, JSON.parse(JSON.stringify(item.parentNodes))) : '';
  })
}
const getChildIds = (data: DataList[], list: (string | number)[]) => {
  data.forEach((item: DataList) => {
    list.push(item.id)
    if (item.children instanceof Array && item.children.length) {
      getChildIds(item.children, list)
    }
  })
}
// 获取分页的改变
const changePage = (data: { pageCurrent: any; pageSize: any }) => {
  emit('changePage', data)
};
//defineExpose
defineExpose({ dataSource })
</script>
<style lang="less" scoped></style>
<style lang="less">
.topLevel,
.dataName {
  .halfSelect {
    .ant-checkbox-inner {
      background: #fff;
      border-color: var(--theme_button_hover_bg_color, #4892fa);
    }

    .ant-checkbox-inner::after {
      position: absolute;
      display: table;
      border: 2px solid var(--theme_button_hover_bg_color, #4892fa);
      border-top: 0;
      border-left: 0;
      transform: rotate(90deg) scale(1) translate(-118%, -15%);
      opacity: 1;
      transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
      content: ' ';
      border-bottom: 0;
    }
  }

  .selected {
    .ant-checkbox-inner {
      background: var(--theme_button_hover_bg_color, #4892fa);
      border-color: var(--theme_button_hover_bg_color, #4892fa);
    }

    .ant-checkbox-inner::after {
      position: absolute;
      display: table;
      border: 2px solid #fff;
      border-top: 0;
      border-left: 0;
      transform: rotate(45deg) scale(1) translate(-50%, -50%);
      opacity: 1;
      transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
      content: ' ';
    }
  }

  .noSelected {
    .ant-checkbox-checked .ant-checkbox-inner::after {
      position: absolute;
      display: table;
      border: 2px solid #fff;
      border-top: 0;
      border-left: 0;
      transform: rotate(45deg) scale(1) translate(-50%, -50%);
      opacity: 0;
      transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
      content: ' ';
    }

    .ant-checkbox-checked .ant-checkbox-inner {
      background: #fff;
      border-color: 1px solid #d9d9d9;
    }
  }

}

.permissionTable {
  background-color: inherit;

  ::deep(th:nth-child(1)) {
    padding-left: 16px;
  }

  .ant-table-thead .ant-table-cell:nth-last-of-type(1) {
    text-align: left;
  }
}
</style>
